#include "generic.h"
// #include "getopt.h"
// #include <sys/timeb.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream.h>
#include <glib.h>
// #include <iostream>

int       NO_VERTICES     = 0;
const int MAX_NO_VERTICES = 100;
double    MIN_ERROR = 100000.0;
double    MAX_ERROR = -1.0;
double    mean_difference = 0.0;
double    residual_error = 0.0;
double    maximum_error = 0.0;
long      global_count = 0;

double vertex_x[MAX_NO_VERTICES], vertex_y[MAX_NO_VERTICES];

double sqr(double v) {
  return v*v;
} // sqr

double dist(double x1, double y1, double x2, double y2)
{
  return sqrt(sqr(x2-x1) + sqr(y2-y1));
} // dist

double CalcDistance(double x, double y, double &position_x, double &position_y, double &sum_alpha)
{
  double d[3];
  double alpha[NO_VERTICES], pi[NO_VERTICES], l[NO_VERTICES];
  double sum_pi = 0.0;

  sum_alpha = 0.0;

  for(int i = 0; i < NO_VERTICES; i++) {

    d[0] = dist(vertex_x[i], vertex_y[i], vertex_x[(i+1) % NO_VERTICES], vertex_y[(i+1) % NO_VERTICES]);
    d[1] = dist(vertex_x[i], vertex_y[i], x, y);
    d[2] = dist(vertex_x[(i+1) % NO_VERTICES], vertex_y[(i+1) % NO_VERTICES], x, y);

    alpha[i] = sqrt(fabs((d[0]+d[1]+d[2])*(d[1]+d[2]-d[0])*(d[2]+d[0]-d[1])*(d[0]+d[1]-d[2])))/4.0;

    sum_alpha += alpha[i];
  } // for

  for(int i = 0; i < NO_VERTICES; i++) {

    pi[i] = 1.0;

    for(int j = 0; j < NO_VERTICES; j++) {

      if(j != i) {

	int subst = i-1;
	if(subst < 0) subst = NO_VERTICES-1;

	if(j != subst) {
	
	  pi[i] *= alpha[j];
	} // if

      } // if      
    } // for

    sum_pi += pi[i];
  } // for

  for(int i = 0; i < NO_VERTICES; i++) {
    l[i] = pi[i] / sum_pi;
  } // for

  // normal position with equal weighting

  double norm_pos_x, norm_pos_y, distance;
  double improved_pos_x, improved_pos_y;

  norm_pos_x = norm_pos_y = 0.0;
  improved_pos_x = improved_pos_y = 0.0;

  for(int i = 0; i < NO_VERTICES; i++) {

    distance = dist(vertex_x[i], vertex_y[i], x, y)+1;

    // distance = 400;

    double error_x, error_y;

    error_x = 0 + ((rand() % (int)distance) - distance/2.0) / 5.0;
    error_y = 0 + ((rand() % (int)distance) - distance/2.0) / 5.0;

    //    error_x = 50;
    //    error_y = 50;

    norm_pos_x += (1.0/(double)NO_VERTICES)*(x+error_x);
    norm_pos_y += (1.0/(double)NO_VERTICES)*(y+error_y);

    improved_pos_x += l[i]*(x+error_x);
    improved_pos_y += l[i]*(y+error_y);

  } // for

  double measure = dist(norm_pos_x, norm_pos_y, improved_pos_x, improved_pos_y);
  // double measure = dist(norm_pos_x, norm_pos_y, x, y);

  mean_difference += measure;
  residual_error += dist(x, y, improved_pos_x, improved_pos_y);
  maximum_error += dist(x, y, norm_pos_x, norm_pos_y);
  global_count++;

  if((global_count) % 100 == 0)
    cout << measure << endl;

  return measure;

} // CalcDistance

int main(int argc, char** argv)
{
    SDL_Surface      *screen, *backup_screen;
    SDL_Event        event;
    SDL_Rect         dst_rect, full_image_rect, scrolled_rect, location;
    int              window_width, window_height, thumb_height, thumb_border_size;
    int              current_x, current_y;
 
    window_width = 1024; window_height = 768;

    // analyze command line

    if(SDL_Init(SDL_INIT_VIDEO) < 0) {
//	cerr << "Could not initialize SDL video" << endl;
	exit(-1);
    } // if
    atexit(SDL_Quit);

    Uint32 flags = 0;

//    flags |= SDL_FULLSCREEN;

    screen = SDL_SetVideoMode(window_width, window_height, 24, flags);

    if(!screen) {
//	cerr << "could not SetVideoMode() - abort" << endl;
	exit(-1);
    } // if

    long size_line = screen->pitch;

    const int MID_X = window_width/2;
    const int MID_Y = window_height/2;
    
    vertex_x[0] = 600; vertex_y[0] = 050;
    vertex_x[1] = 250; vertex_y[1] = 075;
    vertex_x[2] = 050; vertex_y[2] = 400;
    vertex_x[3] = 350; vertex_y[3] = 650;
    vertex_x[4] = 850; vertex_y[4] = 350;
    //    vertex_x[5] = ; vertex_y[5] = ;
    
    NO_VERTICES = 5;

    /*

    int i = 0;
    vertex_x[i] = 50; vertex_y[i] = 50; i++;
    vertex_x[i] = vertex_x[i-1]-sqrt(1250)-20; vertex_y[i] = vertex_y[i-1]+sqrt(1250); i++;
    vertex_x[i] = vertex_x[i-1]; vertex_y[i] = vertex_y[i-1]+50; i++;
    vertex_x[i] = vertex_x[i-1]+sqrt(1250); vertex_y[i] = vertex_y[i-1]+sqrt(1250); i++;
    vertex_x[i] = vertex_x[i-1]+50; vertex_y[i] = vertex_y[i-1]; i++;
    vertex_x[i] = vertex_x[i-1]+sqrt(1250)+50; vertex_y[i] = vertex_y[i-1]-sqrt(1250); i++;
    vertex_x[i] = vertex_x[i-1]; vertex_y[i] = vertex_y[i-1]-50; i++;
    vertex_x[i] = vertex_x[i-1]-sqrt(1250); vertex_y[i] = vertex_y[i-1]-sqrt(1250); i++;

    NO_VERTICES = i;

    */

    /*    
    vertex_x[0] = 50; vertex_y[0] = 50;
    vertex_x[1] = 50; vertex_y[1] = 150;
    vertex_x[2] = 150; vertex_y[2] = 150;
    vertex_x[3] = 150; vertex_y[3] = 50;
    
    NO_VERTICES = 4;
    */

    double polygon_area, mid_x, mid_y;
    double position_x, position_y, sum_alpha;
   
    int min_x = 10000;
    int min_y = 10000;

    int max_x = -10000;
    int max_y = -10000;

    for(int i = 0; i < NO_VERTICES; i++) {
      mid_x += vertex_x[i];
      mid_y += vertex_y[i];

      if(vertex_x[i] < min_x) min_x = (int)vertex_x[i];
      if(vertex_x[i] > max_x) max_x = (int)vertex_x[i];
      if(vertex_y[i] < min_y) min_y = (int)vertex_y[i];
      if(vertex_y[i] > max_y) max_y = (int)vertex_y[i];
    } // for

    mid_x /= NO_VERTICES;
    mid_y /= NO_VERTICES;

    CalcDistance(mid_x, mid_y, position_x, position_y, polygon_area);

    for(int y = min_y; y < max_y; y += 1) {
      for(int x = min_x; x < max_x; x += 1) {

	double weighed_error;

	weighed_error = CalcDistance(x, y, position_x, position_y, sum_alpha);

	int gray = (int)(weighed_error*6);

	if(gray > 255) gray = 255;

	//	gray = 255-gray;

	//	cout << sum_alpha << endl;

	if(sum_alpha <= (polygon_area+1)) {
	  //	if(1==1) {

	  //	  position_x *= 1;
	  //	  position_y *= 1;

	  //	  gray = 255-(int)sqrt(sqr(position_x-x) + sqr(position_y-y))*2;

	  //	  cout << gray << " " << flush;
	  
	  //	cout << position_x << " " << position_y << endl;

	  for(int dy = 0; dy < 2; dy++) {
	    for(int dx = 0; dx < 2; dx++) {

	      ((unsigned char*)(screen->pixels))[((int)x+dx)*3 + ((int)y+dy)*size_line + 0] = gray;
	      ((unsigned char*)(screen->pixels))[((int)x+dx)*3 + ((int)y+dy)*size_line + 1] = gray;
	      ((unsigned char*)(screen->pixels))[((int)x+dx)*3 + ((int)y+dy)*size_line + 2] = gray;

	      //	      ((unsigned char*)(screen->pixels))[((int)position_x+dx)*3 + ((int)position_y+dy)*size_line + 0] = gray;
	      //	      ((unsigned char*)(screen->pixels))[((int)position_x+dx)*3 + ((int)position_y+dy)*size_line + 1] = gray;
	      //	      ((unsigned char*)(screen->pixels))[((int)position_x+dx)*3 + ((int)position_y+dy)*size_line + 2] = gray;

	    } // for
	  } // for
	  	  
	  //  ((unsigned char*)(screen->pixels))[x*3 + y*size_line + 0] = 255;
	  //  ((unsigned char*)(screen->pixels))[x*3 + y*size_line + 1] = 255;
	  //  ((unsigned char*)(screen->pixels))[x*3 + y*size_line + 2] = 255;

	} // if

      } // for

      SDL_UnlockSurface(screen);
      SDL_UpdateRect(screen, 0, y, window_width, 1);
      SDL_LockSurface(screen);

    } // for

    SDL_UnlockSurface(screen);
    SDL_UpdateRect(screen, 0, 0, window_width, window_height);
    SDL_LockSurface(screen);
       
    for(;;) {

      	if((SDL_PollEvent(&event) == 0) && (event.type == SDL_KEYDOWN)) 
      	    goto exit_app;

	switch(event.type) {
		
	    case SDL_MOUSEMOTION:	      
		current_x = event.motion.x;
		current_y = event.motion.y;
		break;
		
	    case SDL_KEYDOWN:
		
	      switch(event.key.keysym.sym) {
		
	      case ' ':
		vertex_x[NO_VERTICES] = current_x;
		vertex_y[NO_VERTICES] = current_y;
		NO_VERTICES++;
		break;
		
	      case 13:
		break;

	      case 27:
	      case 'q':
	      case 'Q':
		goto exit_app;
		
	      default:
		break;
	      } // switch
	      break;

	} // switch

	SDL_UnlockSurface(screen);
	SDL_UpdateRect(screen, 0, 0, window_width, window_height);
	SDL_LockSurface(screen);
    } // for
	    
 exit_app:

    SDL_UnlockSurface(screen);	    
    SDL_FreeSurface(screen);

    cout << "mean difference: " << mean_difference/global_count << endl;
    cout << "residual error    : " << residual_error/global_count << endl;
    cout << "normal error    : " << maximum_error/global_count << endl;

    exit(0);
} // main
